//
//	DRAGFILE.CPP
//
//	How to drag+drop files without OLE 
//	(old style Win31 drag+drop)
//
//	Freeware written by J Brown 2004
//
//	www.catch22.net
//

#define STRICT
#include <windows.h>
#include <tchar.h>
#include <shlobj.h>
#include "resource.h"

#define APPNAME "IDropSource"

#pragma comment(linker, "/OPT:NOWIN98")

HWND		g_hwndMain;
HINSTANCE	g_hInstance;

//
//	Find a drop-target under the mouse cursor
//
HWND GetDropTarget()
{
	POINT pt;
	HWND  hwnd;
	DWORD style;

	// get current cursor position
	GetCursorPos(&pt);

	hwnd = WindowFromPoint(pt);
	ScreenToClient(hwnd, &pt);

	// find a window (or it's parent) that accepts files
	while(hwnd != 0)
	{
		style = GetWindowLong(hwnd, GWL_EXSTYLE);

		if(style & WS_EX_ACCEPTFILES)
			return hwnd;

		hwnd = GetParent(hwnd);
	}

	return 0;
}

//
//
// typedef struct 
// {
//    DWORD pFiles;        // offset of file list
//    POINT pt;            // drop point (client coords)
//    BOOL  fNC;           // is it on NonClient area and pt is in screen coords
//    BOOL  fWide;         // WIDE character switch
//
// } DROPFILES;
//
void DropFiles(HWND hwnd, TCHAR *szFiles[], int nNumFiles)
{
	DROPFILES *drop;
	POINT	   pt;
	TCHAR	  *str;
	int		   i;
	int		   len;

	// find where the cursor is relative to window
	GetCursorPos(&pt);
	ScreenToClient(hwnd, &pt);

	for(i = 0, len = 0; i < nNumFiles; i++)
		len += lstrlen(szFiles[i]) + 1;

	drop = GlobalAlloc(GPTR, sizeof(DROPFILES) + len + 1);

	drop->pFiles = sizeof(DROPFILES);
	drop->pt     = pt;
	drop->fNC    = FALSE;

#ifdef UNICODE
	drop->fWide  = TRUE;
#else
	drop->fWide  = FALSE;
#endif

	for(i = 0, str = (TCHAR *)(drop+1); i < nNumFiles; i++)
	{
		str += wsprintf(str, _T("%s"), szFiles[i]) + 1;
	}

	*str = '\0';

	// The handler for WM_DROPFILES deallocates the "drop" structure for us
	PostMessage(hwnd, WM_DROPFILES, (WPARAM)(HDROP)drop, 0);
}

//
//	How to process WM_DROPFILES
//
void HandleDropFiles(HDROP hDrop)
{
	TCHAR buf[MAX_PATH];
	int  i;
	
	for(i = 0; DragQueryFile(hDrop, i, buf, MAX_PATH); i++)
	{
		MessageBox(0, buf, "Dropped File", MB_ICONINFORMATION);
		OutputDebugString(buf);
	}
	
	DragFinish(hDrop);
}

//
//	This is how WM_DROPFILES works internally
//
BOOL HandleDropFilesManual(HDROP hDrop)
{
	DROPFILES *drop = (DROPFILES *)GlobalLock(hDrop);

	if(drop == 0)
	{
		DragFinish(hDrop);
		return FALSE;
	}
	
	if(drop->fWide)
	{
		WCHAR *str = (WCHAR *)((PBYTE)drop + drop->pFiles);

		for( ; *str; str = str + lstrlenW(str) + 1)
		{
			MessageBoxW(0, str, L"Dropped file", MB_ICONINFORMATION);
			OutputDebugStringW(str);
			OutputDebugStringA("\n");
		}
	}
	else
	{
		char *str = (char *)((PBYTE)drop + drop->pFiles);

		for( ; *str; str = str + lstrlenA(str) + 1)
		{
			MessageBoxA(0, str, "Dropped file", MB_ICONINFORMATION);
			OutputDebugStringA(str);
			OutputDebugStringA("\n");
			
		}
	}
	
	GlobalUnlock(hDrop);
	
	DragFinish(hDrop);

	return FALSE;
}

//
//	Main Window message handler
//
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	static BOOL fMouseDown = FALSE;

	switch(msg)
	{
	case WM_CREATE:
		DragAcceptFiles(hwnd, TRUE);
		return TRUE;

	case WM_DROPFILES:
		HandleDropFiles((HDROP)wParam);
		return 0;

	case WM_LBUTTONDOWN:
		fMouseDown = TRUE;
		SetCapture(hwnd);
		return 0;

	case WM_MOUSEMOVE:
		
		if(fMouseDown)
		{
			if(GetDropTarget() != NULL)
			{
				SetCursor(LoadCursor(g_hInstance, MAKEINTRESOURCE(IDC_CURSOR1)));
			}
			else
			{
				SetCursor(LoadCursor(0, IDC_NO));
			}
		}

		return 0;

	case WM_LBUTTONUP:

		if(fMouseDown)
		{
			char *filelist[] = { "C:\\hello.txt", "C:\\world.txt" };

			DropFiles(GetDropTarget(), filelist, 2);

			fMouseDown = FALSE;
			ReleaseCapture();
		}

		return 0;

	case WM_KEYDOWN:
		if(wParam == VK_ESCAPE)
		{
			fMouseDown = FALSE;
			ReleaseCapture();
		}

		break;

	case WM_COMMAND:
	
		// react to menu messages
		switch(LOWORD(wParam))
		{
		case IDCANCEL:
			return 0;

		case IDM_FILE_EXIT:
			CloseWindow(hwnd);
			return 0;

		case IDM_FILE_ABOUT:
			MessageBox(hwnd, "DragFile Test Application\r\n\r\n"
							 "Copyright(c) 2004 by Catch22 Productions\t\r\n"
							 "Written by J Brown.\r\n\r\n"
							 "Homepage at www.catch22.net", APPNAME, MB_ICONINFORMATION);
			return 0;
		}

		break;

	case WM_CLOSE:

		DragAcceptFiles(hwnd, FALSE);

		// shut program down
		DestroyWindow(hwnd);
		PostQuitMessage(0);
		return 0;

	}

	return DefWindowProc(hwnd, msg, wParam, lParam);
}

void InitMainWnd()
{
	WNDCLASSEX wc = { sizeof(wc) };

	wc.lpfnWndProc   = WndProc;
	wc.lpszClassName = APPNAME;
	wc.lpszMenuName  = MAKEINTRESOURCE(IDR_MENU1);
	wc.hCursor		 = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));
	wc.hInstance     = g_hInstance;
	wc.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE+1);


	RegisterClassEx(&wc);
}

void CreateMainWnd()
{
	g_hwndMain = CreateWindowEx(WS_EX_CLIENTEDGE, APPNAME, APPNAME, 
		WS_VISIBLE|WS_OVERLAPPEDWINDOW, 
		CW_USEDEFAULT, CW_USEDEFAULT, 512, 200, 0,0,
		g_hInstance, 0);
}

int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR lpCmdLine, int nShowCmd)
{
	MSG msg;
	
	g_hInstance = hInst;

	InitMainWnd();
	CreateMainWnd();

	// message-pump
	while(GetMessage(&msg, 0, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return 0;
}
